package com.example.demo.competitor;

import com.example.demo.Application;
import com.example.demo.maintainer.GatewayOfficer;
import com.example.demo.service.ChannelService;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 通道竞争
 * 接收通道消息, 通过数据库事务做通道的竞争,
 * 通道竞争到后, 会把通道信息提交给通道维护器
 * 如果没有竞争到, 会把通道加入黑名单中, 一段时间内不会再去竞争这个通道
 * * Created by guoyangqiao on 10/05/2017.
 */
@Component
public class GatewayCompetitor {

    private static final Logger LOGGER = LoggerFactory.getLogger(GatewayCompetitor.class);

    private static final long BLACK_GATEWAY_EXPIRE_MINUTE = 5;

    private static final Cache<Integer, Object> BLACK_GATEWAYS_CACHE = CacheBuilder.newBuilder().expireAfterWrite(BLACK_GATEWAY_EXPIRE_MINUTE, TimeUnit.MINUTES).build();

    private static final long DELAY_PER_GATEWAY = 5;

    private static GatewayCompetitor gatewayCompetitor;

    @Resource
    private ChannelService channelService;

    @PostConstruct
    public void init(){
        gatewayCompetitor = this;
        gatewayCompetitor.channelService = this.channelService;
    }

    public static void competeGatewayThenNotifySubsequentIfSuccess(final int gatewayId) {
        if (BLACK_GATEWAYS_CACHE.getIfPresent(gatewayId) != null) {
            LOGGER.info("{}通道{}在黑名单,不竞争通道发送权", Application.SERVER_NAME_ID, gatewayId);
        return;
        }
        if (GatewayOfficer.ifGatewayPrivilegeExistsThenRefreshPrivilegeTime(gatewayId)) {
            LOGGER.info("{}通道{}在应用内存在,将通道任务消息发送给通道控制器", Application.SERVER_NAME_ID, gatewayId);
            GatewayOfficer.acceptNewGatewayWork(gatewayId, Application.SERVER_NAME_ID);
        } else {
            LOGGER.info("{}通道{}不在当前通道列表内,竞争通道执行权限", Application.SERVER_NAME_ID, gatewayId);
            boolean doPriorityAcquired = competeDbSendPriority(gatewayId, Application.SERVER_NAME_ID, getCompeteDelay());
            if (doPriorityAcquired) {
                LOGGER.info("{}竞争到通道{}的发送权限,通知通道发送控制器", Application.SERVER_NAME_ID, gatewayId);
                GatewayOfficer.acceptNewGatewayWork(gatewayId, Application.SERVER_NAME_ID);
            } else {
                LOGGER.info("{}没有竞争到通道{}的发送权限,将通道加入黑名单", Application.SERVER_NAME_ID, gatewayId);
                BLACK_GATEWAYS_CACHE.put(gatewayId, new Object());
            }
        }
    }

    private static long getCompeteDelay() {
        return GatewayOfficer.AVAILABLE_GATEWAY.size() * DELAY_PER_GATEWAY;
    }


    private static boolean competeDbSendPriority(int gatewayId, final String serviceName, long gatewayDelay) {
        LOGGER.info("开始竞争通道锁 " + gatewayId);
        //依赖于数据库获取唯一发送权限
        try {
            Thread.sleep(gatewayDelay);
        } catch (Exception e) {
            LOGGER.error(" 竞争线程锁前 休眠异常 continue", e);
        }
        //修改数据库中对应的通道配置
        gatewayCompetitor.channelService.competeSendConfigRight(String.valueOf(gatewayId), serviceName, "");

        //然后通过相同的条件去获取该通道，如果获取不到，说明没有修改成功，竞争通道失败
        String pkid = gatewayCompetitor.channelService.getSendConfigRight(String.valueOf(gatewayId), serviceName);

        if (StringUtils.isEmpty(pkid)) {
            LOGGER.info("没有竞争到通道锁,放弃执行 " + gatewayId);
            return false;
        } else {
            LOGGER.info("竞争到通道锁,继续执行 " + gatewayId);
            return true;
        }
    }
}
